﻿using Model;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Media;
using ME = Model.Environment;

namespace Interpreter
{
	public class Persistence
	{
		private string[] ReadFile(string filename)
		{
			return File.ReadAllLines(filename, Encoding.UTF8);
		}

		public Dictionary<string, CellDefinition> ParseCellDefinitions(string filename)
		{
			var cds = ME.CellDefinitions;
			ME.CellDefinitions = new Dictionary<string, CellDefinition>();
			string[] cellDefs = ReadFile(filename);

			try
			{
				foreach (var cd in cellDefs)
				{
					ParseCellDefinition(cd);
				}
				return ME.CellDefinitions;
			}
			catch (ArgumentException ex)
			{
				ME.CellDefinitions = cds;
				throw ex;
			}
			catch
			{
				ME.CellDefinitions = cds;
				throw new Exception("Die Zelldefinitionen konnten nicht geladen werden");
			}
		}

		public void ParseCellDefinition(string cellDefinitionString)
		{
			string zellExpression = cellDefinitionString.Replace(" ", "");
			String[] nameSeperiert = zellExpression.Split('(');
			string name = nameSeperiert[0];
			string attributListe = nameSeperiert[1].Replace(")", "");

			CellDefinition neueZelle = new CellDefinition(name);

			if (attributListe.Contains(':'))
			{
				string[] stringAttribute = attributListe.Split(',');
				foreach (string tmpAttribut in stringAttribute)
				{
					String[] attributNameSeperiert = tmpAttribut.Split(':');
					string attributName = attributNameSeperiert[0];
					string attributString = attributNameSeperiert[1];

					if (attributString.Equals("string"))
					{
						neueZelle.addAttribut(new StringAttribut(attributName, ""));
					}
					else if (attributString.Equals("double"))
					{
						neueZelle.addAttribut(new NumberAttribut(attributName, 0.0));
					}
					else
					{
						throw new ArgumentException(attributString + " ist ein ungültiger Typ.");
					}
				}
			}

			ME.CellDefinitions.Add(neueZelle.Name, neueZelle);
		}

		public Preferences ParsePreferences(string filename)
		{
			string[] lines = ReadFile(filename);

			Preferences preferences = new Preferences();
			Dictionary<string, string> pairs;
			try
			{
				pairs = lines.Select(line => line.Split(':'))
				.ToDictionary(cells => cells[0].Trim(), cells => cells[1].Trim());
			}
			catch
			{
				throw new Exception("Die Datei hat ein ungültiges Format.");
			}
			foreach (var kvPair in pairs)
			{
				switch (kvPair.Key)
				{
					case "Aktualisierungstyp":
						preferences.ActualizationType = ParseActualizationType(kvPair.Value);
						break;
					case "Zellverteilung":
						preferences.CellDistribution = ParseCellDistribution(kvPair.Value);
						break;
					case "Umgebungstyp":
						preferences.EnvironmentType = ParseEnvironmentType(kvPair.Value);
						break;
					case "InitialFeld":
						preferences.InitialField = ParseInitialField(kvPair.Value);
						break;
					case "Höhe":
						preferences.SizeHeight = ParseSize(kvPair.Key, kvPair.Value);
						break;
					case "Breite":
						preferences.SizeWidth = ParseSize(kvPair.Key, kvPair.Value);
						break;
					case "Aktualisierungsintervall":
						preferences.ActualizationInterval = ParseSize(kvPair.Key, kvPair.Value);
						break;
					case "Zellfarben":
						preferences.ColorsForCells = ParseColorsForCells(kvPair.Value);
						break;
					default:
						throw new ArgumentException(kvPair.Key + " ist ungültig für Einstellungen.");
				}
			}

			return preferences;
		}

		private ActualizationType ParseActualizationType(string type)
		{
			switch (type.ToLower())
			{
				case "sequentiell":
					return ActualizationType.Sequential;
				case "synchron":
					return ActualizationType.Synchron;
				default:
					throw new ArgumentException(type + " ist nicht gültig für ActualizationType.");
			}
		}

		private Dictionary<CellDefinition, double> ParseCellDistribution(string distributionString)
		{
			Dictionary<CellDefinition, double> retPairs;
			try
			{
				retPairs = distributionString.Split(';')
				.Select(entry => entry.Split('-'))
					.ToDictionary(kv => new CellDefinition(kv[0].Trim()), kv => double.Parse(kv[1].Trim()));
			}
			catch
			{
				throw new ArgumentException(distributionString + " gibt keine gültige Definition einer Zellverteilung an.");
			}

			return retPairs;
		}

		private EnvironmentType ParseEnvironmentType(string type)
		{
			switch (type.ToLower())
			{
				case "vonneumann":
					return EnvironmentType.VonNeumann;
				case "moore":
					return EnvironmentType.Moore;
				default:
					throw new ArgumentException(type + " ist nicht gültig für ActualizationType.");
			}
		}

		private Cell[,] ParseInitialField(string field)
		{
			//TODO implementieren
			throw new NotImplementedException("Die Funktion, ein initiales Feld zu laden, bisher nicht implementiert");
		}

		private int ParseSize(string key, string value)
		{
			int retValue;
			if (!int.TryParse(value, out retValue))
			{
				throw new ArgumentException(key + " definiert keinen Ganzzahl-Wert.");
			}
			return retValue;
		}

		private Dictionary<CellDefinition, Color> ParseColorsForCells(string colorsString)
		{
			Dictionary<CellDefinition, Color> retPairs;
			try
			{
				retPairs = colorsString.Split(';')
				.Select(entry => entry.Split('-'))
					.ToDictionary(kv => new CellDefinition(kv[0].Trim()),
					kv => Color.FromRgb(byte.Parse(kv[1].Split(',')[0]), byte.Parse(kv[1].Split(',')[1]), byte.Parse(kv[1].Split(',')[2])));
			}
			catch
			{
				throw new ArgumentException(colorsString + " gibt keine gültige Definition von Zellfarben an.");
			}

			return retPairs;
		}


		public string[] LoadRules(string filename)
		{
			return ReadFile(filename);
		}
	}
}
